package database;

import java.util.*;

public class Table {
    private final String name;
    private final String primaryKey;
    private int rowLimit;
    private LinkedList<String> primaryKeyValues;
    private List<String> header;
    private LinkedList<Map<String, Object>> data;

    public Table(String name, String primaryKey, Collection<String> header, int rowLimit) {
        this.name = name;
        this.primaryKey = primaryKey;
        this.rowLimit = rowLimit;
        this.primaryKeyValues = new LinkedList<>();
        this.header = new ArrayList<>(header);
        this.data = new LinkedList<>();
    }

    public String name() {
        return name;
    }

    public String primaryKey() {
        return primaryKey;
    }

    public List<String> header() {
        return header;
    }

    public boolean insert(Map<String, Object> row) {
        if (!row.containsKey(primaryKey)) {
            return false; // 缺少主键
        }

        String pkv = row.get(primaryKey).toString();
        if (primaryKeyValues.contains(pkv)) {// 主键重复,更新该行
            int index = primaryKeyValues.indexOf(pkv);
            data.set(index, row);
        } else {
            data.add(row);
            primaryKeyValues.add(pkv);
            if (rowLimit != -1 && data.size() > rowLimit) {
                data.removeFirst();
                primaryKeyValues.removeFirst();
            }
        }

        return true;
    }

    public boolean update(Map<String, Object> row) {
        if (!row.containsKey(primaryKey)) {
            return false; // 缺少主键
        }

        String pkv = row.get(primaryKey).toString();

        int index = primaryKeyValues.indexOf(pkv);
        if (index == -1) {// 未找到匹配的行, 插入新行
            data.add(row);
            primaryKeyValues.add(pkv);
            if (rowLimit != -1 && data.size() > rowLimit) {
                data.removeFirst();
                primaryKeyValues.removeFirst();
            }
        } else {
            data.set(index, row);
        }

        return true; // 未找到匹配的行
    }

    public boolean delete(int row, List<Condition> conditions, List<Order> orders) {
        Map<String, Object> rowData = row(row, null, conditions, orders);
        if (rowData.size() == 0) {
            return false;
        }
        int index = primaryKeyValues.indexOf(rowData.get(primaryKey).toString());
        primaryKeyValues.remove(index);
        data.remove(index);
        return true;
    }

    public boolean deletes(int rows, List<Condition> conditions, List<Order> orders) {
        List<Map<String, Object>> rowData = rows(rows, null, conditions, orders);
        if (rowData.size() == 0) {
            return false;
        }
        for (Map<String, Object> row : rowData) {
            int index = primaryKeyValues.indexOf(row.get(primaryKey).toString());
            primaryKeyValues.remove(index);
            data.remove(index);
        }
        return true;
    }

    public Map<String, Object> row(int row, List<String> columns, List<Condition> conditions, List<Order> orders) {
        List<Map<String, Object>> rowData = rows(-1, columns, conditions, orders);
        Map<String, Object> ret = new LinkedHashMap<>();
        if (row < 1 || row > rowData.size()) {
            return ret;
        }
        return rowData.get(row - 1);
    }

    public List<Map<String, Object>> rows(int rows, List<String> columns, List<Condition> conditions, List<Order> orders) {
        List<Map<String, Object>> rowData = new ArrayList<>();
        List<Condition> availableConditions = new ArrayList<>();
        if (conditions != null) {
            for (Condition condition : conditions) {
                if (header.contains(condition.getColumn())) {
                    availableConditions.add(condition);
                }
            }
        }
        List<Order> availableOrders = new ArrayList<>();
        if (orders != null) {
            for (Order order : orders) {
                if (header.contains(order.getColumn())) {
                    availableOrders.add(order);
                }
            }
        }
        for (Map<String, Object> row : data) {
            boolean flag = true;
            for (Condition condition : availableConditions) {
                if (!condition.check(row.get(condition.getColumn()))) {
                    flag = false;
                    break;
                }
            }
            if (!flag) {
                continue;
            }
            Map<String, Object> newRow = new LinkedHashMap<>();
            if (columns == null) {
                newRow = row;
            } else {
                for (String column : columns) {
                    if (!header.contains(column)) {
                        continue;
                    }
                    newRow.put(column, row.get(column));
                }
            }
            rowData.add(newRow);
        }
        if (availableOrders.size() > 0) {
            rowData.sort((o1, o2) -> {
                for (Order order : availableOrders) {
                    int result = 0;
                    switch (order.getOrder()) {
                        case ASC:
                            result = o1.get(order.getColumn()).toString().compareTo(o2.get(order.getColumn()).toString());
                            break;
                        case DESC:
                            result = o2.get(order.getColumn()).toString().compareTo(o1.get(order.getColumn()).toString());
                            break;
                    }
                    if (result != 0) {
                        return result;
                    }
                }
                return 0;
            });
        }
        if (rows < 0 || rows > rowData.size()) {
            return rowData;
        }
        return rowData.subList(0, rows);
    }

    public List<Object> column(int rows, String column,
                               List<Condition> conditions, List<Order> orders) {
        List<Object> ret = new ArrayList<>();
        if (!header.contains(column)) {
            return ret;
        }
        List<Map<String, Object>> rowData = rows(rows, null, conditions, orders);
        for (Map<String, Object> row : rowData) {
            ret.add(row.get(column));
        }
        return ret;
    }

    public Map<String, List<Object>> columns(int rows, List<String> columns,
                               List<Condition> conditions, List<Order> orders) {
        Map<String, List<Object>> ret = new LinkedHashMap<>();
        List<String> availableColumns = new ArrayList<>();
        if (columns != null) {
            for (String column : columns) {
                if (header.contains(column)) {
                    availableColumns.add(column);
                }
            }
        } else {
            availableColumns = header;
        }
        for (String column : availableColumns) {
            ret.put(column, new ArrayList<>());
        }
        List<Map<String, Object>> rowData = rows(rows, null, conditions, orders);
        for (Map<String, Object> row : rowData) {
            for (String column : availableColumns) {
                ret.get(column).add(row.get(column));
            }
        }

        return ret;
    }

    public Object cell(int row, String column,
                       List<Condition> conditions, List<Order> orders) {
        Map<String, Object> rowData = row(row, null, conditions, orders);
        if (rowData.size() == 0 || !header.contains(column)) {
            return "@#$%";
        }
        return rowData.get(column);
    }

    public int count() {
        return data.size();
    }
}
